package demo.guiJFrame;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class Game2048_v4 {
    private JFrame frame;
    private JPanel panel;
    private JButton button;
    private JLabel count, time, maxCount;
    private Grid4[] Grids;
    private int[][] num;
    private boolean ifCanMove = true;
    private int maxCo = 0, nowCount = 0, oldCo = 0, minute = 0, second = 0;
    private TimeThread tt;
    private List list = new List();
    private int[][] oldNum;

    class TimeThread extends Thread {
        int a = 1;

        void stops() {
            a = 0;
        }

        public void run() {
            while (ifCanMove && a == 1) {
                try {
                    if (second > 59) {
                        second = 0;
                        minute++;
                    }
                    time.setText("��ǰ��ʱ " + minute + ":" + second++);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private Game2048_v4() {
        setNum();
        frame = new JFrame("WEY2048");
        frame.setBounds(750, 300, 400, 465);
        frame.setLayout(new BorderLayout());
        panel = new JPanel();
        panel.setSize(400, 400);
        panel.setLayout(new GridLayout(4, 4, 1, 1));
        JPanel record = new JPanel();
        record.setSize(400, 100);
        record.setLayout(new GridLayout(2, 2, 1, 1));
        Grids = new Grid4[16];
        for (int i = 0; i < 16; i++) {
            Grids[i] = new Grid4(num[i / 4][i % 4]);
            panel.add(Grids[i].ret());
        }
        button = new JButton("����Ϸ");
        maxCount = new JLabel("��߷�" + maxCo);
        maxCount.setHorizontalAlignment(JLabel.CENTER);
        maxCount.setFont(new Font("����", Font.BOLD, 16));
        count = new JLabel("��ǰ���� " + nowCount);
        count.setHorizontalAlignment(JLabel.CENTER);
        count.setFont(new Font("����", Font.BOLD, 16));
        time = new JLabel("��ǰ��ʱ " + minute + ":" + second);
        time.setHorizontalAlignment(JLabel.CENTER);
        time.setFont(new Font("����", Font.BOLD, 16));
        addLis();
        record.add(count);
        record.add(maxCount);
        record.add(time);
        record.add(button);
        frame.add(record, BorderLayout.NORTH);
        frame.add(panel, BorderLayout.CENTER);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        panel.requestFocus();
        tt = new TimeThread();
        tt.start();
    }

    private void addLis() {
        button.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                tt.stops();
                setNum();
                flush();
                ifCanMove = true;
                minute = 0;
                second = 0;
                nowCount = 0;
                count.setText("��ǰ���� " + nowCount);
                tt = new TimeThread();
                tt.start();
                panel.requestFocus();
            }
        });
        panel.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                int a = e.getKeyCode();
                if (a == KeyEvent.VK_BACK_SPACE) {
                    Cell cell = list.get();
                    if (cell != null) {
                        for (int i = 0; i < 4; i++)
                            System.arraycopy(cell.num[i], 0, num[i], 0, 4);
                        nowCount = cell.count;
                        flush();
                        ifCanMove = true;
                    }
                    return;
                }
                if (a != KeyEvent.VK_UP && a != KeyEvent.VK_DOWN && a != KeyEvent.VK_LEFT && a != KeyEvent.VK_RIGHT)
                    return;
                if (!ifCanMove) {
                    JOptionPane.showMessageDialog(frame, "���Ѿ���·����");
                    return;
                }
                for (int i = 0; i < 4; i++)
                    System.arraycopy(num[i], 0, oldNum[i], 0, 4);
                oldCo = nowCount;
                boolean isMove;
                if (a == KeyEvent.VK_UP)
                    isMove = GeZiUp();
                else if (a == KeyEvent.VK_DOWN)
                    isMove = GeZiDown();
                else if (a == KeyEvent.VK_LEFT)
                    isMove = GeZiLeft();
                else isMove = GeZiRight();
                if (isMove) {
                    addNum();
                    list.set(new Cell(oldNum, oldCo));
                    flush();
                }
                if (!ifCanMove())
                    JOptionPane.showMessageDialog(frame, "���Ѿ���·����");
            }

            private boolean GeZiRight() {
                boolean ifMoveRight = false;
                for (int i = 0; i < 4; i++)
                    for (int j = 3; j > 0; j--)
                        if ((num[i][j] == 0 && num[i][j - 1] != 0) || (num[i][j] == num[i][j - 1] && num[i][j] != 0))
                            ifMoveRight = true;
                if (!ifMoveRight)
                    return false;
                for (int i = 0; i < 4; i++) {
                    for (int x = 0; x < 4; x++) {
                        for (int j = 3; j > 0; j--) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i][j - 1];
                                num[i][j - 1] = 0;
                            }
                        }
                    }
                    for (int j = 3; j > 0; j--) {
                        if (num[i][j] == num[i][j - 1]) {
                            nowCount += num[i][j];
                            num[i][j] = num[i][j] * 2;
                            num[i][j - 1] = 0;
                        }
                    }
                    for (int x = 0; x < 4; x++) {
                        for (int j = 3; j > 0; j--) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i][j - 1];
                                num[i][j - 1] = 0;
                            }
                        }
                    }
                }
                return true;
            }

            private boolean GeZiLeft() {
                boolean ifMoveLeft = false;
                for (int i = 0; i < 4; i++)
                    for (int j = 0; j < 3; j++)
                        if ((num[i][j] == 0 && num[i][j + 1] != 0) || (num[i][j] == num[i][j + 1]) && num[i][j] != 0)
                            ifMoveLeft = true;
                if (!ifMoveLeft)
                    return false;
                for (int i = 0; i < 4; i++) {
                    for (int x = 0; x < 4; x++) {
                        for (int j = 0; j < 3; j++) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i][j + 1];
                                num[i][j + 1] = 0;
                            }
                        }
                    }
                    for (int j = 0; j < 3; j++) {
                        if (num[i][j] == num[i][j + 1]) {
                            nowCount += num[i][j];
                            num[i][j] = num[i][j] * 2;
                            num[i][j + 1] = 0;
                        }
                    }
                    for (int x = 0; x < 4; x++) {
                        for (int j = 0; j < 3; j++) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i][j + 1];
                                num[i][j + 1] = 0;
                            }
                        }
                    }
                }
                return true;
            }

            private boolean GeZiDown() {
                boolean ifMoveDown = false;
                for (int j = 0; j < 4; j++)
                    for (int i = 3; i > 0; i--)
                        if ((num[i][j] == 0 && num[i - 1][j] != 0) || (num[i][j] == num[i - 1][j] && num[i][j] != 0))
                            ifMoveDown = true;
                if (!ifMoveDown)
                    return false;
                for (int j = 0; j < 4; j++) {
                    for (int x = 0; x < 4; x++) {
                        for (int i = 3; i > 0; i--) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i - 1][j];
                                num[i - 1][j] = 0;
                            }
                        }
                    }
                    for (int i = 3; i > 0; i--) {
                        if (num[i][j] == num[i - 1][j]) {
                            nowCount += num[i][j];
                            num[i][j] = num[i][j] * 2;
                            num[i - 1][j] = 0;
                        }
                    }
                    for (int x = 0; x < 4; x++) {
                        for (int i = 3; i > 0; i--) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i - 1][j];
                                num[i - 1][j] = 0;
                            }
                        }
                    }
                }
                return true;
            }

            private boolean GeZiUp() {
                boolean ifMoveUp = false;
                for (int j = 0; j < 4; j++)
                    for (int i = 0; i < 3; i++)
                        if ((num[i][j] == 0 && num[i + 1][j] != 0) || (num[i][j] == num[i + 1][j] && num[i][j] != 0))
                            ifMoveUp = true;
                if (!ifMoveUp)
                    return false;
                for (int j = 0; j < 4; j++) {
                    for (int x = 0; x < 4; x++) {
                        for (int i = 0; i < 3; i++) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i + 1][j];
                                num[i + 1][j] = 0;
                            }
                        }
                    }
                    for (int i = 0; i < 3; i++) {
                        if (num[i][j] == num[i + 1][j]) {
                            nowCount += num[i][j];
                            num[i][j] = num[i][j] * 2;
                            num[i + 1][j] = 0;
                        }
                    }
                    for (int x = 0; x < 4; x++) {
                        for (int i = 0; i < 3; i++) {
                            if (num[i][j] == 0) {
                                num[i][j] = num[i + 1][j];
                                num[i + 1][j] = 0;
                            }
                        }
                    }
                }
                return true;
            }
        });
    }

    private void flush() {
        for (int i = 0; i < 16; i++) {
            Grids[i].update(num[i / 4][i % 4]);
        }
        if (nowCount > maxCo)
            maxCo = nowCount;
        count.setText("��ǰ���� " + nowCount);
        maxCount.setText("��߷�" + maxCo);
    }

    private void setNum() {
        num = new int[4][4];
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                num[i][j] = 0;
        for (int i = 0; i < 5; i++)
            addNum();
        list.format();
        oldNum = new int[4][4];
        for (int i = 0; i < 4; i++)
            System.arraycopy(num[i], 0, oldNum[i], 0, 4);
    }

    private void addNum() {
        int m = (int) (Math.random() * 4);
        int n = (int) (Math.random() * 4);
        while (num[m][n] != 0) {
            m = (int) (Math.random() * 4);
            n = (int) (Math.random() * 4);
        }
        num[m][n] = 2;
    }

    private boolean ifCanMove() {
        if (!ifCanMove)
            return false;
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                if (num[i][j] == 0)
                    return true;
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 3; j++)
                if (num[i][j] == num[i][j + 1] || num[j][i] == num[j + 1][i])
                    return true;
        ifCanMove = false;
        return false;
    }

    public static void main(String[] args) {
        new Game2048_v4();
    }
}

class Grid4 {
    private JLabel lab;

    Grid4(int num) {
        lab = new JLabel();
        lab.setHorizontalAlignment(JLabel.CENTER);
        lab.setFont(new Font("����", Font.BOLD, 20));
        lab.setOpaque(true);
        set(num);
    }

    private void set(int num) {
        String str = num + "";
        if (num == 0)
            str = "";
        lab.setText(str);
        switch (num) {
            case 2:
                lab.setBackground(new Color(238, 228, 218));
                break;
            case 4:
                lab.setBackground(new Color(237, 224, 200));
                break;
            case 8:
                lab.setBackground(new Color(242, 177, 121));
                break;
            case 16:
                lab.setBackground(new Color(245, 149, 99));
                break;
            case 32:
                lab.setBackground(new Color(246, 124, 95));
                break;
            case 64:
                lab.setBackground(new Color(246, 94, 59));
                break;
            case 128:
                lab.setBackground(new Color(237, 207, 114));
                break;
            case 256:
                lab.setBackground(new Color(237, 204, 97));
                break;
            case 512:
                lab.setBackground(new Color(237, 200, 80));
                break;
            case 1024:
                lab.setBackground(new Color(237, 197, 63));
                break;
            case 2048:
                lab.setBackground(new Color(237, 194, 46));
                break;
            default:
                lab.setBackground(Color.WHITE);
        }
    }

    void update(int num) {
        set(num);
    }

    JLabel ret() {
        return lab;
    }
}

class List {
    private final int max = 7;
    private Cell[] cells = new Cell[max];

    void format() {
        for (int i = 0; i < max; i++)
            cells[i] = null;
    }

    public void set(Cell cell) {
        System.arraycopy(cells, 0, cells, 1, max - 1);
        cells[0] = cell;

    }

    public Cell get() {
        Cell ret = cells[0];
        System.arraycopy(cells, 1, cells, 0, max - 1);
        cells[max - 1] = null;
        return ret;
    }
}

class Cell {
    int[][] num = new int[4][4];
    int count;

    Cell(int[][] n, int count) {
        for (int i = 0; i < 4; i++)
            System.arraycopy(n[i], 0, num[i], 0, 4);
        this.count = count;
    }
}
